package com.gframework.mybatis.dao.datasource;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import com.gframework.context.spring.AppContext;
import com.gframework.util.GStringUtils;
import com.gframework.util.GUtils;

/**
 * 多数据源注解方式使用aop包装类
 * 
 * @since 2.0.0
 * @author Ghwolf
 */
@Aspect
@Order(Integer.MIN_VALUE)
public class MultipartDataSourceAspect {

	private final Map<String,String> dbCache = new ConcurrentHashMap<>();

	@Around("@annotation(com.gframework.mybatis.dao.datasource.DataSource) || @within(com.gframework.mybatis.dao.datasource.DataSource)")
	public Object around(ProceedingJoinPoint join) throws Throwable{
		String id = dbCache.computeIfAbsent(join.toLongString(), k->getDataSourceId(join));
		try {
			RoutingDataSource.alwaysSetDataSource(id);
			return join.proceed();
		} finally {
			RoutingDataSource.stopAlwaysSetDataSource();
		}
	}
	
	/**
	 * 获取dataSourceId
	 */
	private String getDataSourceId(ProceedingJoinPoint join){
		MethodSignature methodSign = (MethodSignature) join.getSignature();
		Method method = methodSign.getMethod();
		DataSource ds = AnnotationUtils.findAnnotation(method, DataSource.class);
		if (ds == null) {
			ds = AnnotationUtils.findAnnotation(method.getDeclaringClass(), DataSource.class);
		}
		if (ds == null) {
			throw new IllegalStateException("该方法：" + methodSign.toLongString() + " 及其所属类不存在 "
					+ DataSource.class.getName() + " 注解，无法执行数据源修改代理操作！");
		}
		
		String idExp = ds.value();
//		
		SpelExpressionParser spel = new SpelExpressionParser();
		
		
		String exp = GUtils.handleParam(idExp, "${", "}", AppContext :: getProperty);
		return GUtils.handleParam(exp, "#{", "}", v->GStringUtils.toString(spel.parseRaw(v).getValue()));
		
	}
	
	
}
